﻿using System;

namespace EnterpriseLibraryExtensions.Events.Aggregation
{
    /// <summary>
    /// CompareEvent is the base class to derive from when implementing event comparers.
    /// Even though it is possible to create your own class and implement the IEventComparer interface, it is more work and not typesafe.
    /// </summary>
    /// <typeparam name="TEventToCompare">The type of event your derived class compares.</typeparam>
    public abstract class CompareEvent<TEventToCompare> : IEventComparer<TEventToCompare>
    {
        /// <summary>
        /// Determines whether this instance can handle events of the specified type.
        /// </summary>
        /// <param name="type">The type to test for.</param>
        /// <returns>
        /// true if this instance can handle the event type; otherwise false.
        /// </returns>
        public bool CanHandleEventType(Type type)
        {
            return type == typeof(TEventToCompare);
        }

        /// <summary>
        /// Compares two objects and returns a value indicating whether one is less than, equal to, or greater than the other.
        /// </summary>
        /// <param name="x">The first object to compare.</param>
        /// <param name="y">The second object to compare.</param>
        /// <returns>
        /// A signed integer that indicates the relative values of <paramref name="x"/> and <paramref name="y"/>, as shown in the following table.Value Meaning Less than zero <paramref name="x"/> is less than <paramref name="y"/>. Zero <paramref name="x"/> equals <paramref name="y"/>. Greater than zero <paramref name="x"/> is greater than <paramref name="y"/>.
        /// </returns>
        /// <exception cref="T:System.ArgumentException">Neither <paramref name="x"/> nor <paramref name="y"/> implements the <see cref="T:System.IComparable"/> interface.-or- <paramref name="x"/> and <paramref name="y"/> are of different types and neither one can handle comparisons with the other. </exception>
        public int Compare(object x, object y)
        {
            if (x == null)
            {
                throw new ArgumentNullException("x");
            }

            if (y == null)
            {
                throw new ArgumentNullException("y");
            }

            CheckType(x, "x");
            CheckType(y, "y");

            return IsMatch((TEventToCompare)x, (TEventToCompare)y) ? 0 : 1;
        }

        /// <summary>
        /// Determines whether the specified left is match. In the derived class return true if left and right are matching.
        /// </summary>
        /// <param name="left">The left value to check.</param>
        /// <param name="right">The right value to check.</param>
        /// <returns>
        /// <c>true</c> if left and right matches; otherwise, <c>false</c>.
        /// </returns>
        protected abstract bool IsMatch(TEventToCompare left, TEventToCompare right);

        /// <summary>
        /// Checks if the instance provided is of the type TEventToCompare. If it is not an exception is thrown.
        /// </summary>
        /// <param name="instance">The instance to examine the type of.</param>
        /// <param name="paramName">Name of the param to include in the exception if it is raised.</param>
        /// <exception cref="System.ArgumentException">Thrown if instance is not of the correct type.</exception>
        private static void CheckType(object instance, string paramName)
        {
            if (instance.GetType() != typeof(TEventToCompare))
            {
                throw new ArgumentException("The parameter is of an unexpected type.", paramName);
            }
        }
    }
}
